package org.fast.cms.common.utils.time;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import lombok.Getter;

/** 
* 
* 日期工具类
* @author weigen.ye 
* @date 创建时间：2017年6月16日 上午11:37:35 
*/
public final class DateUtils {

    //默认的日期格式
    public static final String DEFAULT_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

    private DateUtils() {

    }
    
    /**
     * 把字符串转化为日期<br>
     * @param date 日期字符串
     * @param pattern 日期格式字符串 如：yyyy-MM-dd，yyyy-MM-dd HH:mm:ss，yyyyMMddHHmmss等
     * @return 不能解析返回null
     */
    public static Date parseDate(final String date, final String pattern) {
        try {
            return org.apache.commons.lang3.time.DateUtils.parseDate(date, new String[] {pattern});
        } catch (ParseException e) {
            return null;
        }
    }
    
    /**
     * 把字符串转化成格式为yyyy-MM-dd HH:mm:ss的日期<br>
     * 参数为null返回null，不能解析返回null
     * @param date 日期字符串
     * @return Date
     */
    public static Date parseDate(final String date) {
        if (date == null) {
            return null;
        }
        try {
            return org.apache.commons.lang3.time.DateUtils.parseDate(date, new String[] {DEFAULT_TIME_FORMAT});
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 按照给定格式返回代表日期的字符串<br>
     * 没有对format参数做合格法校验，如果format不正确结果不一定 <br>
     * format为 yyyy-MM-dd HH:mm:ss 返回的是24小时制结果，<br>
     * format为 yyyy-MM-dd hh:mm:ss 返回的是12小时制结果，<br>
     * @param date  Date
     * @param format String 日期格式
     * @return String 代表日期的字符串
     */
    public static String formatDate(final Date date, final String format) {
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        return sdf.format(date);
    }
    
    /**
     * 返回 日期格式为yyyy-MM-dd HH:mm:ss格式的日期字符串，<br>
     * @param date 
     * @return
     */
    public static String formatDate(final Date date) {
    	return formatDate(date,DEFAULT_TIME_FORMAT);
    }
    
    /**
     * 获取指定日期date的指定时间hour，指定时间hour超过小时制范围，<br>
     * 返回结果中的日期将改变，结果不改变参数date的值。
     * @param date 指定日期
     * @param hour 指定时间 
     * @param metric 24小时制或12小时制枚举
     * @return Date对象
     */
    public static Date getSpecialHourOfDay(final Date date,final Integer hour,final HourMetric metric){
    	Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(metric.getMaxHour(), hour);
        Date result = c.getTime();
        return result;
    }
    
    /**
     * 计算相对年，amount可以为负数
     * @param date 日期对象
     * @param amount 相对大小
     * @return
     */
    public static Date addYears(final Date date, int amount) {
        return org.apache.commons.lang3.time.DateUtils.addYears(date, amount);
    }

   /**
     * 计算相对月，amount可以为负数
     * @param date 日期对象
     * @param amount 相对大小
     * @return
     */
    public static Date addMonths(final Date date, int amount) {
        return org.apache.commons.lang3.time.DateUtils.addMonths(date, amount);
    }
    
    /**
     * 计算相对星期，amount可以为负数
     * @param date 日期对象
     * @param amount 相对大小
     * @return
     */
    public static Date addWeeks(final Date date, int amount) {
        return org.apache.commons.lang3.time.DateUtils.addWeeks(date, amount);
    }

    /**
     * 计算相对日，amount可以为负数
     * @param date 日期对象
     * @param amount 相对大小
     * @return
     */
    public static Date addDays(final Date date, int amount) {
        return org.apache.commons.lang3.time.DateUtils.addDays(date, amount);
    }
    
    /**
     * 计算相对小时，amount可以为负数
     * @param date 日期对象
     * @param amount 相对大小
     * @return 
     */
    public static Date addHours(final Date date, int amount) {
        return org.apache.commons.lang3.time.DateUtils.addHours(date, amount);
    }
    
    /**
     * 计算相对分钟，amount可以为负数
     * @param date 日期对象
     * @param amount 相对大小
     * @return
     */
    public static Date addMinutes(final Date date, int amount) {
        return org.apache.commons.lang3.time.DateUtils.addMinutes(date, amount);
    }
    
    /**
     * 计算相对秒，amount可以为负数
     * @param date 日期对象
     * @param amount 相对大小
     * @return
     */
    public static Date addSeconds(final Date date, int amount) {
        return org.apache.commons.lang3.time.DateUtils.addSeconds(date, amount);
    }
    
    /**
     * 计算相对毫秒，amount可以为负数
     * @param date 日期对象
     * @param amount 相对大小
     * @return
     */
    public static Date addMilliseconds(final Date date, int amount) {
        return org.apache.commons.lang3.time.DateUtils.addMilliseconds(date, amount);
    }

   
    /**
     * 计算时间跨度，结束时间减开始时间，不足时间计为0
     * @param begin 开始时间
     * @param end 结束时间
     * @param type 时间类型 
     * @return
     */
    public static long getTimeSpan(final Date begin, final Date end, final TimeType type) {
        long diff = end.getTime() - begin.getTime();
        switch (type) {
            case DAY:
            default:
                return diff / org.apache.commons.lang3.time.DateUtils.MILLIS_PER_DAY;
            case HOUR:
                return diff / org.apache.commons.lang3.time.DateUtils.MILLIS_PER_HOUR;
            case MINUTE:
                return diff / org.apache.commons.lang3.time.DateUtils.MILLIS_PER_MINUTE;
            case SECOND:
                return diff / org.apache.commons.lang3.time.DateUtils.MILLIS_PER_SECOND;
        }
    }

    /**
     * 获得end日期与begin日期相差天数,不足一天的忽略
     * @param begin 开始时间
     * @param end 结束时间
     * @return 相差天数
     */
    public static long getDaySpan(final Date begin, final Date end) {
        return getTimeSpan(begin, end, TimeType.DAY);
    }

    /**
     * 获得end日期与begin日期相差小时,不足一小时的忽略
     * @param begin 开始时间
     * @param end 结束时间
     * @return 相差小时
     */
    public static long getHourSpan(final Date begin, final Date end) {
        return getTimeSpan(begin, end, TimeType.HOUR);
    }

    /**
     * 获得end日期与begin日期相差分钟,不足一分钟的忽略
     * @param begin 开始时间
     * @param end 结束时间
     * @return 相差分钟
     */
    public static long getMinuteSpan(final Date begin,final Date end) {
        return getTimeSpan(begin, end, TimeType.MINUTE);
    }

    /**
     * 获得end日期与begin日期相差秒数,不足一秒的忽略
     * @param begin 开始时间
     * @param end 结束时间
     * @return 相差秒数
     */
    public static long getSecondSpan(final Date begin, final Date end) {
        return getTimeSpan(begin, end, TimeType.SECOND);
    }

    /**
     * 获得end日期与begin日期相差月份,不足一月的忽略
     * @param begin 开始时间
     * @param end 结束时间
     * @return 相差月份数
     */
    public static int getMonthSpan(final Date begin, final Date end) {
        Calendar beginCal = new GregorianCalendar();
        beginCal.setTime(begin);
        Calendar endCal = new GregorianCalendar();
        endCal.setTime(end);
        int m = (endCal.get(Calendar.MONTH)) - (beginCal.get(Calendar.MONTH));
        int y = (endCal.get(Calendar.YEAR)) - (beginCal.get(Calendar.YEAR));
        return y * 12 + m;
    }

    /**
     * 获取当前时间当月的第一天<br>
     * @param date
     * @return 
     */
    public static Date getFirstDayOfMonth(final Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int firstDay = calendar.getActualMinimum(Calendar.DAY_OF_MONTH);
        return org.apache.commons.lang3.time.DateUtils.setDays(date, firstDay);
    }
    
    /**
     * 获取当前时间当月的最后一天<br>
     * @param date
     * @return
     */
    public static Date getLastDayOfMonth(final Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int firstDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        return org.apache.commons.lang3.time.DateUtils.setDays(date, firstDay);
    }

    /**
     * 获取当前时间本周的第一天<br>
     * @param date
     * @return
     */
    public static Date getFirstDayOfWeek(final Date date) {
        return getDayOfWeek(date, Week.MONDAY);
    }
    
   /**
    * 获取当前时间对应星期的某一星期，星期从星期一开始，星期天结束<br>
    * 参数date不变
    * @param date
    * @param dayOfWeek
    * @return
    */
    public static Date getDayOfWeek(final Date date, final Week dayOfWeek) {
    	Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int week = calendar.get(Calendar.DAY_OF_WEEK);
        int temp=dayOfWeek.getType();
        if (week == dayOfWeek.getType()) {
            return calendar.getTime();
        }
        int diffDay = temp - week;
        if (week == Calendar.SUNDAY) {
            diffDay -= 7;
        } else if (temp == Calendar.SUNDAY) {
            diffDay += 7;
        }
        calendar.add(Calendar.DATE, diffDay);
        return calendar.getTime();
    }

    /**
     * 获取计算完相对时间后对应的yyyy-MM-dd HH:mm:ss 日期字符串
     * @param dateStr 日期字符串
     * @param pattern 日期格式
     * @param addDays 计算相对时间，可为负数
     * @return
     */
    public static String parseFullFormat(final String dateStr, final String pattern, int addDays) {
        try {
            Date date = parseDate(dateStr, pattern);
            if (addDays != 0) {
                date = addDays(date, addDays);
            }
            return DateUtils.formatDate(date, "yyyy-MM-dd HH:mm:ss");
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 返回yyyy-MM-dd HH:mm:ss格式的时间字符串
     * @param date
     * @return
     */
    public static String getDateStr(final Date date) {
    	return DateUtils.formatDate(date, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 生成java.util.Date类型的对象
     * @param year  int 年
     * @param month int 月
     * @param day   int 日
     * @return Date java.util.Date类型的对象
     */
    public static Date getDate(int year, int month, int day) {
        GregorianCalendar d = new GregorianCalendar(year, month - 1, day);
        return d.getTime();
    }

    /**
     * 将yyyyMMdd形式日期数字，转化为日期对象<br>
     * 未校验参数 yyyyMMdd的格法性如-20001201，2300
     * 调用方必须保证传入参数为yyyyMMdd形式日期数字
     * @param yyyyMMdd
     * @return
     */
    public static Date getDate(int yyyyMMdd) {
        int dd = yyyyMMdd % 100;
        int yyyyMM = yyyyMMdd / 100;
        int mm = yyyyMM % 100;
        int yyyy = yyyyMM / 100;
        Calendar calendar=Calendar.getInstance();
        //月份从0开始算
        calendar.set(yyyy, mm - 1, dd);
        return calendar.getTime();
    }
    
    /**
     * 生成圆整至小时的参数date对应时间 例如：若参数date时间为（2004-08-01 11:30:58）<br>
     * 将获得（2004-08-01 11:00:00）的日期对象
     * @param date
     * @return
     */
    public static Date getRoundedHourDate(final Date date){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.clear(Calendar.MINUTE);
        calendar.clear(Calendar.SECOND);
        calendar.clear(Calendar.MILLISECOND);
        return calendar.getTime();
    }
    
    /**
     * 生成圆整至小时的当前时间 例如：若当前时间为（2004-08-01 11:30:58）<br>
     * 将获得（2004-08-01 11:00:00）的日期对象
     * @return Date java.util.Date对象
     */
    public static Date getRoundedHourCurDate() {
        return getRoundedHourDate(new Date());
    }

    /**
     * 得到参数date的开始时间<br>
     * 例如：若参数date为（2004-08-01 11:30:58）<br>
     * 将获得（2004-08-01 00:00:00）的日期对象
     * @param date
     * @return
     */
    public static Date getStartOfDate(final Date date) {
        if (date == null) {
            return date;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(date.getTime());
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
    
    /**
     * 得到参数date的结束时间<br>
     * 例如：若参数date为（2004-08-01 11:30:58）<br>
     * 将获得（2004-08-01 23:59:59）的日期对象
     * @param date
     * @return
     */
    public static Date getEndOfDate(final Date date) {
        if (date == null) {
            return date;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(date.getTime());
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        return calendar.getTime();
    }

    /**
     * 获得参数date，amonut天后或天前零时的日期对象
     * @param date
     * @param amonut 为正为后，为负为前
     * @return
     */
    public static Date getSpacialDayBeginTime(Date date,int amonut){
    	 if (date == null) {
             return date;
         }
         date=DateUtils.addDays(date, amonut);
         Calendar calendar = Calendar.getInstance();
         calendar.setTimeInMillis(date.getTime());
         calendar.set(Calendar.HOUR_OF_DAY, 0);
         calendar.set(Calendar.MINUTE, 0);
         calendar.set(Calendar.SECOND, 0);
         calendar.set(Calendar.MILLISECOND, 0);
         return calendar.getTime();
    }
    
    /**
     * 获得参数date，amonut天后或天前最后时间的日期对象  <br> 
     * 
     * @param date
     * @param amonut 为正为后，为负为前
     * @return
     */
    public static Date getSpacialDayEndTime(Date date,int amonut){
    	 if (date == null) {
             return date;
         }
         date=DateUtils.addDays(date, amonut);
         Calendar calendar = Calendar.getInstance();
         calendar.setTimeInMillis(date.getTime());
         calendar.set(Calendar.HOUR_OF_DAY, 23);
         calendar.set(Calendar.MINUTE, 59);
         calendar.set(Calendar.SECOND, 59);
         return calendar.getTime();
    }
    
    /**
     * 获得参数date的第二天零时的日期对象 <br> 
     * 例如：若给定时间为（2004-08-01 11:30:58），将获得（2004-08-02 00:00:00）的日期对象  <br>
     * 若给定时间为（2004-08-31 11:30:58），将获得（2004-09-01 00:00:00）的日期对象
     * @param date  给定的java.util.Date对象
     * @return 
     */
    public static Date getNextDayBeginTime(Date date) {
    	return DateUtils.getSpacialDayBeginTime(date, 1);
    }
    
    /**
     * 获得参数date的第二天最后时间的日期对象 <br> 
     * 例如：若给定时间为（2004-08-01 11:30:58），将获得（2004-08-02 23:59:59）的日期对象  <br>
     * 若给定时间为（2004-08-31 11:30:58），将获得（2004-09-01 23:59:59）的日期对象
     * @param date  给定的java.util.Date对象
     * @return 
     */
    public static Date getNextDayEndTime(Date date) {
    	return DateUtils.getSpacialDayEndTime(date, 1);

    }
    
    /**
     * 获得参数date的第二天零时的日期对象 <br> 
     * 例如：若给定时间为（2004-08-01 11:30:58），将获得（2004-08-02 00:00:00）的日期对象  <br>
     * 若给定时间为（2004-08-31 11:30:58），将获得（2004-09-01 00:00:00）的日期对象
     * @param date  给定的java.util.Date对象
     * @return 
     */
    public static Date getPreviousBeginTime(Date date) {
        return DateUtils.getSpacialDayBeginTime(date,-1);
    }
    
    /**
     * 获得参数date的第二天最后时间的日期对象 <br> 
     * 例如：若给定时间为（2004-08-01 11:30:58），将获得（2004-08-02 23:59:59）的日期对象  <br>
     * 若给定时间为（2004-08-31 11:30:58），将获得（2004-09-01 23:59:59）的日期对象
     * @param date  给定的java.util.Date对象
     * @return 
     */
    public static Date getPreviousEndTime(Date date) {
        return DateUtils.getSpacialDayEndTime(date, -1);
    }
    
    /**
     * 得到结束时间与开始时间之间相差的天数，不足一天的部分忽略
     * @param startDate 开始时间
     * @param endDate 结束时间
     * @return
     */
    public static long getBetweenDate(Date startDate, Date endDate) {
        long startDateTime = startDate.getTime();
        long endDateTime = endDate.getTime();
        long dayTime = 24 * 60 * 60 * 1000;
        long days = (endDateTime - startDateTime) / dayTime;
        return days;
    }

    /**
     * 验证字符串是不是合法的日期；严格判断日期格式YYYYMMDD的正则表达式：包括闰年的判断、大月小月的判断
     * @param dateString 待验证的日期字符串
     * @return 满足则返回true，不满足则返回false
     */
    public static boolean validateDateString(String dateString) {
        if (dateString == null || dateString.equals("")) {
            return false;
        }
        // 日期格式YYYYMMDD的正则表达式,世纪年为闰年，如2000
        String regDate = "^(((([02468][048])|([13579][26]))[0]{2})(02)(([0][1-9])|([1-2][0-9])))" +
                             // 世纪年不为闰年如2100
                             "|(((([02468][1235679])|([13579][01345789]))[0]{2})(02)(([0][1-9])|([1][0-9])|([2][0-8])" +
                             "))" +
                             // 非世纪年为闰年，如1996
                             "|(([0-9]{2}(([0][48])|([2468][048])|([13579][26])))(02)(([0][1-9])|([1-2][0-9])))" +
                             // 非世纪年不为闰年，如1997
                             "|(([0-9]{2}(([02468][1235679])|([13579][01345789])))(02)(([0][1-9])|([1][0-9])|" +
                             "([2][0-8])))" +
                             // 大月，有31天
                             "|(([0-9]{4})(([0]{1}(1|3|5|7|8))|10|12)(([0][1-9])|([1-2][0-9])|30|31))" +
                             // 小月，只有30天
                             "|(([0-9]{4})(([0]{1}(4|6|9))|11)(([0][1-9])|([1-2][0-9])|30))$";
        return dateString.matches(regDate);
    }
    
    /**
     * 判定一个日期specificDate是否在指定的起止日期startDate，endDate之间 <br>
     * 如果isStrict为false，将startDate时间设为 00:00:00 <br>
     * 将startDate时间设为 23:59:59 <br>
     * @param startDate
     * @param endDate
     * @param specificDate
     * @param isStrict 
     * @return
     */
    public static boolean isBetweenDate(Date startDate, Date endDate, Date specificDate, Boolean isStrict){
    	if(isStrict==null||isStrict==false){
    		startDate=DateUtils.getSpacialDayBeginTime(startDate, 0);
    		endDate=DateUtils.getSpacialDayEndTime(endDate, 0);
    	}
    	long startTime=startDate.getTime();
    	long endTime=endDate.getTime();
    	long specialTime=specificDate.getTime();
    	if (startTime <= specialTime && endTime >= specialTime) {
            return true;
        }
    	return false;
    }
    
    public static boolean isBetweenDate(Date startDate, Date endDate, Date specificDate){
    	return isBetweenDate(startDate,endDate,specificDate,true);
    }

    @Getter
    public enum HourMetric{
        HOUR_24("24小时制",Calendar.HOUR_OF_DAY), 
        HOUR_12("12小时制",Calendar.HOUR);
    	private String key;
		private Integer maxHour;
		HourMetric(String key,Integer maxHour){
    		this.key=key;
    		this.maxHour=maxHour;
    	}
    }
    
    @Getter
    public enum TimeType{
    	SECOND("秒",0), MINUTE("分",1),HOUR("时",2),DAY("天",3);
    	private String key;
		private Integer type;
		TimeType(String key,Integer type){
    		this.key=key;
    		this.type=type;
    	}
    }
    
    @Getter
    public enum Week{
    	SUNDAY("星期天",1),MONDAY("星期一",2),TUESDAY("星期二",3),WEDNESDAY("星期三",4),
    	THURSDAY("星期四",5),FRIDAY("星期五",6),SATURDAY("星期六",7);
    	private String key;
		private Integer type;
		Week(String key,Integer type){
    		this.key=key;
    		this.type=type;
    	}
    }
    
}

